import * as path from 'path';
import * as step from '@flow-step/step-toolkit'
import process from "process";
import * as sinon from 'sinon';
import {expect} from 'chai'
import {afterEach, beforeEach} from 'mocha'
// @ts-ignore
import * as install from "../src/install";
import {getToolSuffix, exportGoEnvs, checkVersionValid} from "../src/install";

let sandbox = sinon.createSandbox()
let exportEnvVar: sinon.SinonStub;
let addPath: sinon.SinonStub;
let stepToolInstall: sinon.SinonStub;
let stepToolFormatToLatestPatchVersionStub: sinon.SinonStub;

beforeEach(() => {
    sandbox.stub(process, 'env').value({
        ...process.env,
        FLOW_TOOL_REGISTRY_URL: 'https://tool-registry-url.com',
        FLOW_RUNNER_TOOL_CACHE_DIR: '/tmp/flow_tools'
    })

    exportEnvVar = sandbox.stub(step, 'exportEnvVar')
    addPath = sandbox.stub(step, 'addPath')
    stepToolInstall = sandbox.stub(step.tool, 'install')
    stepToolFormatToLatestPatchVersionStub = sandbox.stub(step.tool, 'formatToLatestPatchVersion')
})

afterEach(() => {
    sandbox.restore()
    process.env.FLOW_TOOL_REGISTRY_URL = ''
    process.env.FLOW_RUNNER_TOOL_CACHE_DIR = ''
})

describe('install map go version', () => {
    it('should return go version when input not in map', () => {
        expect(install.mapGOVersion("1.22.1")).to.equal("1.22.1")
    })

    it('should return correct version', () => {
        expect(install.mapGOVersion("1.8.x")).to.equal("1.8")
        expect(install.mapGOVersion("1.9.x")).to.equal("1.9")
        expect(install.mapGOVersion("1.11.x")).to.equal("1.11")
        expect(install.mapGOVersion("1.12.x")).to.equal("1.12")
        expect(install.mapGOVersion("1.13.x")).to.equal("1.13")
        expect(install.mapGOVersion("1.14.x")).to.equal("1.14")
        expect(install.mapGOVersion("1.15.x")).to.equal("1.15")
        expect(install.mapGOVersion("1.16.x")).to.equal
        expect(install.mapGOVersion("1.17.x")).to.equal("1.17")
        expect(install.mapGOVersion("1.18.x")).to.equal("1.18")
        expect(install.mapGOVersion("1.19.x")).to.equal("1.19")
        expect(install.mapGOVersion("1.20.x")).to.equal("1.20")
        expect(install.mapGOVersion("1.21.x")).to.equal("1.21")
    })
})

describe('setupGO', () => {
    it('should correctly setup GOROOT, GOPATH, and add bin to path', () => {
        const goCachedPath = '/tmp/go/cache/path';
        const goPath = '/tmp/go/path';

        const setupGO = install.setupGO
        setupGO(goCachedPath, goPath);

        sinon.assert.calledWith(exportEnvVar, 'GOROOT', goCachedPath)
        sinon.assert.calledOnceWithExactly(addPath, path.join(goCachedPath, 'bin'))
        sinon.assert.calledWith(exportEnvVar, 'GOPATH', goPath)
    });
});

describe('installGO', () => {
    it('installs Go successfully', async () => {
        const inputGOVersion = '1.21.x';
        const goPath = '/tmp/go/path';
        const mappedVersion = '1.21';
        const latestPatchVersion = '1.21.9';
        stepToolFormatToLatestPatchVersionStub.resolves(latestPatchVersion);

        const goCachedPath = '/tmp/go/1.17.1';
        stepToolInstall.resolves(goCachedPath);

        await install.installGO(inputGOVersion, goPath);

        sinon.assert.calledWith(stepToolFormatToLatestPatchVersionStub, 'go', '', mappedVersion);
        sinon.assert.calledWith(stepToolInstall, 'go', latestPatchVersion, getToolSuffix(step.platform.getPlatform()));
        sinon.assert.calledWith(exportEnvVar, 'GOROOT', goCachedPath)
        sinon.assert.calledOnceWithExactly(addPath, path.join(goCachedPath, 'bin'))
        sinon.assert.calledWith(exportEnvVar, 'GOPATH', goPath)
    });

});

describe('exportGoEnvs', () => {
    it('should set GO111MODULE=on and GOPROXY=https://goproxy.cn for latestPatchVersion >= 1.13', () => {
        const latestPatchVersion = '1.13.0';
        exportGoEnvs(latestPatchVersion);

        sinon.assert.calledWith(exportEnvVar, 'GO111MODULE', 'on')
        sinon.assert.calledWith(exportEnvVar, 'GOPROXY', 'https://goproxy.cn')
    });

    it('should set GO111MODULE=on and GOPROXY=https://goproxy.cn for latestPatchVersion < 1.13', () => {
        const latestPatchVersion = '1.12.0';
        exportGoEnvs(latestPatchVersion);

        sinon.assert.neverCalledWith(exportEnvVar, 'GO111MODULE', 'on')
        sinon.assert.calledWith(exportEnvVar, 'GOPROXY', 'https://goproxy.cn')
    });

    it('should not set GOPROXY=https://goproxy.cn when useForeignCluster is true', () => {
        sandbox.stub(process, 'env').value({
            ...process.env,
            "useForeignCluster": "true",
        })

        const latestPatchVersion = '1.13.0';
        exportGoEnvs(latestPatchVersion);

        sinon.assert.calledWith(exportEnvVar, 'GO111MODULE', 'on')
        sinon.assert.neverCalledWith(exportEnvVar, 'GOPROXY', 'https://goproxy.cn')
    });
});

describe('checkVersionValid', () => {
    it('should return true for valid versions greater than or equal to 1.6.0', () => {
        expect(checkVersionValid('1.6')).to.be.true;
        expect(checkVersionValid('1.6.0')).to.be.true;
        expect(checkVersionValid('1.7.0')).to.be.true;
        expect(checkVersionValid('1.18.3')).to.be.true;
    });

    it('should return false for invalid versions', () => {
        expect(checkVersionValid('1.5.0')).to.be.false;
        expect(checkVersionValid('')).to.be.false;
    });
});